/*!
   \file

   © 2011 Mark W. Gabby II

   This file is part of the Avion engine.

   The Avion engine is free software: you can redistribute it and/or modify it under the terms of
   the GNU General Public License as published by the Free Software Foundation, either version 3 of
   the License, or (at your option) any later version.

   The Avion engine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
   the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with the Avion Engine.
   If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <list>
#include <vector>

#include "Util/typedefs.hpp"

#include "Util/ObjectStore.hpp"

class Component;

/*!
   \brief
      SystemBase is used for systems with components. It provides a basic component-handling system
      so that this doesn't have to be recoded for each system.

      This includes functions that add and remove components, a list of components to delete, and a
      delete queued components function that is automatically called after the SystemUpdate
      function by the Update function.
*/
template< typename baseComponentType >
class SystemBase
{
public:
   /*!
       \brief
           Override this function to initialize your class.
   */
   virtual bool Initialize() = 0;

   /*!
      \brief
         This function should be called to perform an per-frame updates for your system.
         It calls your custom SystemUpdate function, which should contain the actual system behavior.

      \param Elapsed
         Amount of time elapsed, in seconds, since the last update.
   */
   void Update( double Elapsed )
   {
      // Delete any components that were queued
      // for delete last update
      DeleteQueuedComponents();

      // Do derived class updates
      SystemUpdate( Elapsed );

      // Update on all our components
      UpdateComponents( Elapsed );
   }

   /*!
       \brief
           This function should be called when your system is closed. It calls your custom
           SystemClose function, which should contain the actual system behavior.

           The Close() function deletes all components.
   */
   void Close()
   {
      // First, handle derived class closing
      SystemClose();

      // Go through our components and remove each one
      typename ObjectStore< baseComponentType >::iterator it;

      while ( systemComponents.Size() )
      {
         it = systemComponents.Begin();
         Component * ToDeallocate = ( *it ).second;
         DeallocateComponent( ToDeallocate );
      }

      systemComponents.Clear();
   }

   /*!
       \brief
           This function returns a pointer to a component belonging to this system from a handle.

       \param Handle
           The component's handle.
   */
   Component * GetComponent( uint32_t Handle )
   {
      // Hash by handle
      return systemComponents[Handle];
   }

   /*!
       \brief
           This function is called to delete a component. If preferred, you can use the component's
           Delete function instead. Both should have the same effect.

       \param ToDelete
           A pointer to the component to delete.
   */
   void DeleteComponent( baseComponentType * ToDelete )
   {
      // Queue it up - don't delete it immediately, that could
      // cause problems
      QueueComponentForDelete( ToDelete );
   }

protected:
   /*!
       \brief
           You should overload this function to perform your system's update.

       \param Elapsed
           The amount of time elapsed, in seconds, since the last update.
   */
   virtual void SystemUpdate( double ElapsedSeconds ) = 0;

   /*!
       \brief
       This function should be overriden to perform any uninitializing work for your system.
   */
   virtual void SystemClose() = 0;

   /*!
       \brief
           This function should be overrided to references to a component from any lists of concrete
           components you have.

           Really, you should have lists of each type of concrete component.

           When this function is called, it means that that component is about to be deallocated
           just a few instructions from now.

       \param ToRemove
           The component to remove from the lists.
   */
   virtual void RemoveComponentFromSpecificLists( const Component * ToRemove ) = 0;

   /*!
       \brief
           This function is protected for a reason. You should have "AddConcreteComponent" functions
           for every type of concrete component you have in your system. In those functions, you
           should first call AddComponent to get the handle to the new component, and then add the
           new component to specific component lists. I suggest a map of handles to concrete
           component pointers.

       \param ToAdd
           A pointer to the component you want to add.
   */
   uint32_t AddComponent( baseComponentType * ToAdd )
   {
      // Simply add the component to our list
      return systemComponents.AddObject( ToAdd );
   }

   /*!
       \brief
           Apparently, virtual destructors are important for pure virtual classes.
   */
   virtual ~SystemBase() {}
private:

   /*!
       \brief
           Calls Update() on each component in the system.
   */
   void UpdateComponents( double Elapsed )
   {
      // Go through each component and calls its update function
      typename ObjectStore< baseComponentType >::iterator it;

      for ( it = systemComponents.Begin(); it != systemComponents.End(); ++it )
      {
         baseComponentType * CurComponent = ( *it ).second;
         CurComponent->Update( Elapsed );
      }
   }


   /*!
       \brief
           Adds a component to the delete queue. DeleteComponent does this
           deleting work automatically, so don't worry about this.

       \param ToQueue
           The component to add to the delete queue.
   */
   void QueueComponentForDelete( Component * ToQueue )
   {
      // Add it to the "To Delete" list
      componentsToDelete.push_back( ToQueue );
   }

   /*!
       \brief
           Goes through the components that are queued to be deleted and does so. If you call the
           Update functions on your systems, this is done automatically at the very end of said
           update.
   */
   void DeleteQueuedComponents()
   {
      // Go through each component in the list of components to delete and do the dirty deed
      std::list<Component *>::iterator toDelete;

      for ( toDelete = componentsToDelete.begin(); toDelete != componentsToDelete.end(); ++toDelete )
      {
         DeallocateComponent( ( *toDelete ) );
      }

      // Clear the list, we're done!
      componentsToDelete.clear();
   }


   /*!
       \brief
           This function does the dirty work of actually deleting a component.

       \note
           Defined in Enity.hpp.
   */
   void DeallocateComponent( Component * ToDeallocate );

   // The system's components.
   ObjectStore<baseComponentType> systemComponents;

   // List of components that should be deleted after this update.
   std::list<Component *> componentsToDelete;
};

/*!
    \brief
    This function allows concrete components to be removed from a list.

    \param List
    The list from which to remove the component.

    \param ToRemove
    The component to remove.

    \return
    True if a component was found and deleted, false otherwise.

    \note
    This doesn't deallocate the component - it just removes it.
*/
template<typename type>
bool RemoveComponentFromList( std::list<type *> & List, const Component * ToRemove )
{
   typename std::list<type *>::iterator curComponentIt;

   for ( curComponentIt = List.begin(); curComponentIt != List.end(); ++curComponentIt )
   {
      type * curComponent = ( *curComponentIt );

      if ( curComponent == ToRemove )
      {
         List.erase( curComponentIt );
         return true;
      }
   }

   return false;
}
